package no.uio.ifi.cop.cl;

import java.util.HashMap;

/**
 * A Substitution holds a Map &lt;Variable, Term&gt;,
 * and some utility functions.
 *
 * @version 1.0
 * @author bjarneh@ifi.uio.no
 */


public class Substitution implements Cloneable{

    private HashMap<Variable, Term> terms;

    public Substitution(){
        terms = new HashMap<Variable, Term>();
    }

    public Term get(Variable v){
        return terms.get(v);
    }

    public boolean add(Variable v, Term t){

        Term alreadyHere = terms.get(v);

        if( alreadyHere == null ){
            terms.put(v, t);
            return true;
        }
        
        if( alreadyHere.equals(t) ){
            return true;
        }

        return false;
    }

    // non-destructive clone, 'this' and 'other' are
    // left just as they were prior to join..
    public Substitution join(Substitution other){

        Substitution klone = other.clone();

        for( Variable v: terms.keySet() ){
            if( ! klone.add( v, terms.get(v) ) ){
                return null;
            }
        }

        return klone;
    }

    // shallow clone
    public Substitution clone(){
        Substitution klone = new Substitution();
        for(Variable v: terms.keySet()){
            //klone.add(v.clone(), terms.get(v));
            klone.add(v, terms.get(v));
        }
        return klone;
    }

    public String toString(){
        StringBuilder sb = new StringBuilder();
        for(Variable v : terms.keySet()){
            sb.append(String.format("{%s <- %s}", v, terms.get(v)));
        }
        if(sb.length() == 0){ return "{Ø}"; }
        return sb.toString();
    }

    private boolean compareMap(HashMap<Variable, Term> map){

        Term t1, t2;

        if( terms.size() != map.size() ){
            return false;
        }

        for( Variable v : map.keySet() ){
            if( ! terms.containsKey(v) ){
                return false;
            }else{

                t1 = map.get(v);
                t2 = terms.get(v);

                if( ! t1.equals( t2 ) ){
                    return false;
                }
            }
        }

        return true;
    }

    public boolean equals(Object other){

        if( other instanceof Substitution ){
            Substitution s_other = (Substitution) other;
            return s_other.compareMap( terms );
        }
        return false;
    }

    public int hashCode(){
        return toString().hashCode();
    }
}
